package cn.com.callback

import cn.com.elite.EliteBean
import cn.com.elite.Util
import cn.com.kh.KHDBConfig
import cn.com.kh.KHUtil
import net.sf.json.JSONObject
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.jdbc.core.JdbcTemplate

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.nio.charset.StandardCharsets
import java.security.MessageDigest

/**
 * @projectName: ngsapitask-hst
 * @package: cn.com.callback
 * @className: apiEsopChatOauth
 * @author: LiJT
 * @description: TODO
 * @date: 2024-02-26 09:36
 * @version: 1.0
 */

public class apiEsopChatOauth implements ServiceBackInvoke {
    private static final Log log = LogFactory.getLog(apiEsopChatOauth.class);

    // 初始化参数
    private static String ESOP_Chat_OAuth2_Authorize = "";
    private static String ESOP_Chat_OAuth2_Token = "";
    private static String ESOP_Chat_OAuth2_Userinfo = "";
    private static String ESOP_Chat_OAuth2_Redirect_uri = "";
    private static String ESOP_Chat_OAuth2_Webchat = "";

    public String doCallBack(EliteBean eliteBean, HttpServletRequest request, HttpServletResponse response) {

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");

        String getMethod = request.getMethod();
        log.debug("getMethod:" + getMethod);

        // 获取所有Header
        Map<String, String> mapHeader = new HashMap<String, String>();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            log.debug("-Header-" + key + ":" + value);

            mapHeader.put(key, value);
        }

        // 获取所有参数名
        Map<String, String> mapParams = new HashMap<String, String>();
        Enumeration ep = request.getParameterNames();
        while (ep.hasMoreElements()) {
            String key = (String) ep.nextElement();
            String value = request.getParameter(key);
            log.debug("#apiMiniPControl Parameter-" + key + ":" + value);
            mapParams.put(key, value);

        }
        log.debug("***原始GET请求参数:" + mapParams.toString());

        // 获取数据源
        JdbcTemplate jt = new JdbcTemplate(eliteBean.getDataSource());

        // 初始化地址(这样初始化只需要第一次查询表,后面不重启的话就不用查询表)
        ESOP_Chat_OAuth2_Authorize = KHUtil.isNull(ESOP_Chat_OAuth2_Authorize) ? KHDBConfig.getAPIURL(eliteBean, "ESOP_Chat_OAuth2_Authorize") : ESOP_Chat_OAuth2_Authorize;
        ESOP_Chat_OAuth2_Token = KHUtil.isNull(ESOP_Chat_OAuth2_Token) ? KHDBConfig.getAPIURL(eliteBean, "ESOP_Chat_OAuth2_Token") : ESOP_Chat_OAuth2_Token;
        ESOP_Chat_OAuth2_Userinfo = KHUtil.isNull(ESOP_Chat_OAuth2_Userinfo) ? KHDBConfig.getAPIURL(eliteBean, "ESOP_Chat_OAuth2_Userinfo") : ESOP_Chat_OAuth2_Userinfo;
        ESOP_Chat_OAuth2_Redirect_uri = KHUtil.isNull(ESOP_Chat_OAuth2_Redirect_uri) ? KHDBConfig.getAPIURL(eliteBean, "ESOP_Chat_OAuth2_Redirect_uri") : ESOP_Chat_OAuth2_Redirect_uri;
        ESOP_Chat_OAuth2_Webchat = KHUtil.isNull(ESOP_Chat_OAuth2_Webchat) ? KHDBConfig.getAPIURL(eliteBean, "ESOP_Chat_OAuth2_Webchat") : ESOP_Chat_OAuth2_Webchat;

        // 通过state前缀判断是否是网聊OAuth2.0
        if (GetMapV(mapParams, "type") == "1") {
            // TODO 网聊OAuth2.0认证
            // 获取调用参数
            String device = GetMapV(mapParams, "device").toLowerCase();
            mapParams.put("device", device);
            String state = GetMapV(mapParams, "state");
            state = KHUtil.isNull(state) ? "CHAT" + KHUtil.getRandomString(32) : state;

            // 访客模式
            if (GetMapV(mapParams, "visitor") == "1") {
                String visitorURL = ESOP_Chat_OAuth2_Webchat + "?" + getQueryString(mapParams);
                log.debug("网聊OAuth2.0认证 访客模式 URL:" + visitorURL);
                return visitorURL;
            } else {

                // 获取code_challenge
                String code_verifier = KHUtil.getRandomString(16);
                String code_challenge = generateCodeChallenge(code_verifier);
                String nonce = KHUtil.getRandomString(36);

                // 判断State是否存在如果已经存在返回错误
                String selectExistSQL = '''Select Rec_Guid From Kh_Oauth2_Cache Where State = ?''';
                List<Map<String, Object>> selectExistResult = jt.queryForList(selectExistSQL, state);
                log.debug("判断State是否存在 结果:" + selectExistResult.toString());

                if (selectExistResult.size() <= 0) {
                    String insertSQL = '''Insert Into Kh_Oauth2_Cache (Rec_Guid, Code_Verifier, Code_Challenge, Device, State, Del_flag, Call_Params) Values (?, ?, ?, ?, ?, 0, ?)''';
                    int insertRes = jt.update(insertSQL, code_verifier, code_verifier, code_challenge, device, state, getQueryString(mapParams));

                    String authorization_endpoint = ESOP_Chat_OAuth2_Authorize;
                    authorization_endpoint = authorization_endpoint.replace("{{redirect_uri}}", URLEncoder.encode(ESOP_Chat_OAuth2_Redirect_uri, "utf-8"));
                    authorization_endpoint = authorization_endpoint.replace("{{code_challenge}}", code_challenge);
                    authorization_endpoint = authorization_endpoint.replace("{{state}}", state);
                    authorization_endpoint = authorization_endpoint.replace("{{nonce}}", nonce);
                    log.debug("网聊OAuth2.0认证 返回Authorize_URL:" + authorization_endpoint);
                    return authorization_endpoint;

                } else {
                    log.debug("网聊OAuth2.0认证 State已经存在返回错误:" + "State已经使用");
                    return "State已经使用";
                }
            }

        } else {
            // TODO 网聊OAuth2.0认证回调
            String state = GetMapV(mapParams, "state");
            String code = GetMapV(mapParams, "code");
            String language = GetMapV(mapParams, "language");

            if (!KHUtil.isNull(code)) {

                String selectExistSQL = '''Select Rec_Guid, Code_Verifier, Device, State, Call_Params From Kh_Oauth2_Cache Where State = ?''';
                List<Map<String, Object>> selectExistResult = jt.queryForList(selectExistSQL, state);
                log.debug("判断State是否存在 结果:" + selectExistResult.toString());

                if (selectExistResult.size() <= 0) {
                    log.debug("网聊OAuth2.0认证 State不存在返回错误:" + state);
                    return "State不存在";

                } else {
                    String Rec_Guid = selectExistResult.get(0).get("Rec_Guid").toString();
                    String Device = selectExistResult.get(0).get("Device").toString();
                    String code_verifier = selectExistResult.get(0).get("code_verifier").toString();
                    String Call_Params = selectExistResult.get(0).get("Call_Params").toString();

                    // 获取Token
                    String tokenURL = ESOP_Chat_OAuth2_Token;

                    Map<String, String> headMap = new HashMap<>();
                    headMap.put("Content-Type", "application/x-www-form-urlencoded");

                    Map<String, String> params = new HashMap<>();
                    params.put("client_id", "kuangheng-webchat");
                    params.put("client_secret", "secret");
                    params.put("grant_type", "authorization_code");
                    params.put("redirect_uri", ESOP_Chat_OAuth2_Redirect_uri);
                    params.put("code_verifier", code_verifier);
                    params.put("code", code);

                    // 发送请求获取Token
                    String tokenRes = Util.doHttpPostUseHead(tokenURL, map2Form(params), "UTF-8", 10000, 0, headMap);
                    log.debug("发送请求获取Token 结果:" + tokenRes);

                    JSONObject tokenJson = JSONObject.fromObject(tokenRes);

                    if (tokenJson.containsKey("access_token")) {

                        String access_token = KHUtil.GetSFJV(tokenJson, "access_token");
                        String refresh_token = KHUtil.GetSFJV(tokenJson, "refresh_token");
                        String expires_in = KHUtil.GetSFJV(tokenJson, "expires_in");

                        // 记录Access_Token到数据库
                        jt.update("Update Kh_Oauth2_Cache Set Access_Token = ?, Refresh_Token = ?, Expires_In = ? Where Rec_Guid = ?", access_token, refresh_token, expires_in, Rec_Guid);

                        // 获取用户信息
                        String userinfoURL = ESOP_Chat_OAuth2_Userinfo;

                        // 获取用户信息
                        Map<String, String> userinfoMap = new HashMap<>();
                        userinfoMap.put("Authorization", "Bearer " + access_token);

                        // 发送请求获取用户信息
                        String userinfoRes = Util.doHttpGetUseHead(userinfoURL, "UTF-8", 10000, 0, userinfoMap);
                        log.debug("发送请求获取用户信息 结果:" + userinfoRes);

                        JSONObject userinfoJson = JSONObject.fromObject(userinfoRes);

                        // 解析用户信息
                        String sub = KHUtil.GetSFJV(userinfoJson, "sub");
                        String role = KHUtil.GetSFJV(userinfoJson, "role");

                        // 记录用户信息到数据库
                        jt.update("Update Kh_Oauth2_Cache Set sub = ?, role = ? Where Rec_Guid = ?", sub, role, Rec_Guid);

                        String loginURL = ESOP_Chat_OAuth2_Webchat;
                        if (Device == "mobile") {
                            loginURL = loginURL + "?device=mobile&theme=module&from=APP&userid=" + Rec_Guid + "&" + Call_Params;

                        } else {
                            loginURL = loginURL + "?device=pc&theme=module&from=PC&userid=" + Rec_Guid + "&" + Call_Params;

                        }
                        return loginURL;

                    } else {
                        log.debug("网聊OAuth2.0认证 解析Token错误:回调中参数不存在");
                        return "网聊OAuth2.0认证 解析Token错误:回调中参数不存在";
                    }
                }
            } else {
                log.debug("网聊OAuth2.0认证 解析Code错误:回调中参数不存在");
                return "网聊OAuth2.0认证 解析Code错误:回调中参数不存在";

            }
        }

    }

    // 解析Mapn值(此方法增加需要解析的key不存在保护)
    public static String GetMapV(Map inMap, String getKey) {
        String Rev_Value = null;
        try {
            Rev_Value = String.valueOf(inMap.get(getKey));
        } catch (Exception e) {

            Rev_Value = "";
        } finally {
            if (Rev_Value == null || Rev_Value.equals("null") || Rev_Value.length() <= 0) {
                Rev_Value = "";
            }
        }
        return Rev_Value;
    }

    // 获取codeChallenge
    public static String generateCodeChallenge(String codeVerifier) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hashedBytes = digest.digest(codeVerifier.getBytes(StandardCharsets.UTF_8));

        // Base64 URL safe encoding without padding characters
        return Base64.getUrlEncoder().withoutPadding().encodeToString(hashedBytes);
    }

    // hashMap 转化成表单字符串
    public static String map2Form(HashMap<String, String> map) {
        StringBuilder stringBuilder = new StringBuilder();
        if (map == null) {
            return stringBuilder.toString();
        } else {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            return stringBuilder.substring(0, stringBuilder.length() - 1);
        }
    }
    // 工具方法
    public static String getQueryString(Map<String, String> params) {
        String url = "";
        if (params == null) {
            return url;
        }

        StringBuilder builder = new StringBuilder(url);

        int i = 0;
        for (String key : params.keySet()) {
            String value = params.get(key);
            if (value == null) { // 过滤空的key
                continue;
            }

            if (i != 0) {
                builder.append('&');
            }

            builder.append(key);
            builder.append('=');
            builder.append(encode(value));

            i++;
        }

        return builder.toString();
    }

    // 工具方法
    public static String encode(String input) {
        if (input == null) {
            return "";
        }

        try {
            return URLEncoder.encode(input, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return input;
    }
}